/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pl.cezary.butler.walili.server.db.tasks;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.hamcrest.CoreMatchers;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.hamcrest.beans.HasPropertyWithValue;

/**
 *
 * @author Czarek
 */
public class Matchers {

    public static Matcher isEmpty() {
        return new TypeSafeMatcher<Collection>(Collection.class) {

            @Override
            public boolean matchesSafely(Collection item) {
                return item.isEmpty();
            }

            @Override
            public void describeTo(Description description) {
                description.appendText("is empty");
            }
        };
    }

    public static Matcher hasLength(final int length) {
        return new TypeSafeMatcher<Collection>(Collection.class) {

            @Override
            public boolean matchesSafely(Collection item) {
                return item.size() == length;
            }

            @Override
            public void describeTo(Description description) {
                description.appendText("has length");
                description.appendValue(length);
            }
        };
    }

    public static Matcher hasEqualProperties(final Object comparedTo, String... properties) {
        Set<String> properites = new HashSet<String>(Arrays.asList(properties));
        try {
            BeanInfo info = Introspector.getBeanInfo(comparedTo.getClass(), Object.class);
            PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
            List<Matcher> propMatchers = new ArrayList<Matcher>(Math.min(properites.size(), descriptors.length));
            for (PropertyDescriptor descriptor : descriptors) {
                if (properites.contains(descriptor.getName())) {
                    final Object result = descriptor.getReadMethod().invoke(comparedTo);
                    propMatchers.add(HasPropertyWithValue.hasProperty(descriptor.getName(), CoreMatchers.equalTo(result)));
                }
            }
            return CoreMatchers.allOf((List) propMatchers);
        } catch (Exception ex) {
            Logger.getLogger(Matchers.class.getName()).log(Level.SEVERE, null, ex);
            throw new RuntimeException(ex);
        }
    }

    public static Matcher hasAllEqualProperties(final Object comparedTo) {
        try {
            BeanInfo info = Introspector.getBeanInfo(comparedTo.getClass(), Object.class);
            PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
            List<Matcher> propMatchers = new ArrayList<Matcher>(descriptors.length);
            for (PropertyDescriptor descriptor : descriptors) {
                final Object result = descriptor.getReadMethod().invoke(comparedTo);
                propMatchers.add(HasPropertyWithValue.hasProperty(descriptor.getName(), CoreMatchers.equalTo(result)));
            }
            return CoreMatchers.allOf((List) propMatchers);
        } catch (Exception ex) {
            Logger.getLogger(Matchers.class.getName()).log(Level.SEVERE, null, ex);
            throw new RuntimeException(ex);
        }
    }

    public static Matcher<Integer> divisible(final Integer i) {
        return new Divisible(i);
    }

    private static class Divisible extends TypeSafeMatcher<Integer> {

        private final Integer by;

        public Divisible(final Integer by) {
            super(Integer.class);
            this.by = by;
        }

        @Override
        public boolean matchesSafely(Integer item) {
            return item.intValue() % by.intValue() == 0;
        }

        @Override
        public void describeTo(Description description) {
            description.appendText("is divisible");
        }
    }
}
